package com.cdb.ftp;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.ftp.FTPSClient;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

public class Main {

    /**
     * 入口方法
     * <ul>
     *     <li>参数0：本地路径（待上传的文件或目录，必穿）</li>
     *     <li>参数1：远程目标文件目录（必须穿）</li>
     *     <li>-host：远程ftp服务host（ip或域名，缺省为127.0.0.1）</li>
     *     <li>-port：远程ftp服务端口（缺省为22）</li>
     *     <li>-username：远程ftp服务的用户名（缺省为匿名登录）</li>
     *     <li>-password：远程ftp服务的密码（缺省为无密码）</li>
     * </ul>
     * @param args
     */
    public static void main(String[] args) throws IOException {
        if (args == null || args.length < 2) {
            throw new RuntimeException(String.format("缺少必要的启动参数！"));
        }
        File file = new File(args[0]);
        if (!file.exists() || !file.isFile()) {
            throw new RuntimeException(String.format("文件%s不存在或是个目录！", args[0]));
        }
        //远程目标文件目录
        String destDictionary = args[1];
        if (destDictionary.startsWith("-")) {
            throw new RuntimeException(String.format("目标文件路径无效：%s", destDictionary));
        }
        //ftp参数
        FtpParameter parameter = getParameter(getParameterMap(args));
        //执行上传
        FTPSClient ftpClient = null;
        try {
            ftpClient = getFtpClient(parameter);
            if (!uploadFile(ftpClient, destDictionary, file)) {
                throw new RuntimeException("文件上传失败！");
            }
        } finally {
            if (ftpClient != null) {
                closeFtpClient(ftpClient);
            }
        }
    }

    /**
     * 递归获取目录下的所有文件
     * @param file
     * @return
     */
//    private static List<File> getFilesByFile(File file) {
//        List<File> fileList = new LinkedList<>();
//        if (file.isDirectory()) {
//            File[] files = file.listFiles();
//            for (File f : files) {
//                if (f.isFile()) {
//                    fileList.add(f);
//                } else {
//                    fileList.addAll(getFilesByFile(f));
//                }
//            }
//        } else {
//            fileList.add(file);
//        }
//        return fileList;
//    }

    /**
     * 解析ftp参数
     * @param args
     * @return
     */
    private static Map<String, String> getParameterMap(String[] args) {
        Map<String, String> paramMap = new HashMap<>(11);
        for (int i = 2; i < args.length; i += 1) {
            String parameter = args[i];
            if (parameter.startsWith("-")) {
                if (i + 1 < args.length) {
                    String value = args[i + 1];
                    if (!value.startsWith("-")) {
                        paramMap.put(parameter.substring(1), value);
                    }
                }
            }
        }
        return paramMap;
    }

    /**
     * 获取ftp参数对象
     * @param map
     * @return
     */
    private static FtpParameter getParameter(Map<String, String> map) {
        FtpParameter ftpParameter = new FtpParameter();
        map.forEach((k, v) -> {
            if (k.equals("host")) {
                ftpParameter.setHost(v);
            } else if (k.equals("port")) {
                ftpParameter.setPort(Integer.parseInt(v));
            } else if (k.equals("username")) {
                ftpParameter.setUsername(v);
            } else if (k.equals("password")) {
                ftpParameter.setPassword(v);
            } else {
                throw new RuntimeException("不支持的参数：" + k);
            }
        });
        return ftpParameter;
    }

    /**
     * 上传文件到ftp服务器
     * @param ftpClient ftp客户端
     * @param destDictionary 目标路径
     * @param file 文件
     * @return
     */
    private static boolean uploadFile(FTPClient ftpClient, String destDictionary, File file) throws IOException {
        InputStream inputStream = null;
        try {
            if (ftpClient.changeWorkingDirectory(destDictionary)) {
                inputStream = new FileInputStream(file);
                return ftpClient.storeFile(file.getName(), inputStream);
            }
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

    /**
     * 获取ftp客户端
     * @param parameter
     * @return
     * @throws IOException
     */
    private static FTPSClient getFtpClient(FtpParameter parameter) throws IOException {
        FTPSClient ftpClient = new FTPSClient(false);
        ftpClient.setConnectTimeout(1000 * 30);//设置连接超时时间
        ftpClient.setControlEncoding("utf-8");//设置ftp字符集
        ftpClient.enterLocalPassiveMode();//设置被动模式，文件传输端口设置
        ftpClient.setDefaultPort(parameter.getPort());
        ftpClient.connect(parameter.getHost(), parameter.getPort());
        ftpClient.login(parameter.getUsername(), parameter.getPassword());
        ftpClient.execPROT("P");
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);//设置文件传输模式为二进制，可以保证传输的内容不会被改变
        int replyCode = ftpClient.getReplyCode();
        if (!FTPReply.isPositiveCompletion(replyCode)) {
            ftpClient.disconnect();
            throw new RuntimeException("未连接到FTP，用户名或密码错误!");
        } else {
            return ftpClient;
        }
    }

    /**
     * 关闭ftp客户端
     * @param ftpClient
     */
    private static void closeFtpClient(FTPClient ftpClient) {
        try {
            ftpClient.logout();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                ftpClient.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
